package widget.utils;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.support.annotation.NonNull;
import android.support.v4.widget.SwipeRefreshLayout;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.method.ScrollingMovementMethod;
import android.util.TypedValue;
import android.view.ActionMode;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.widget.AbsListView;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;

import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

import library.Library;
import library.logger.Logger;
import library.utils.DisplayUtil;
import widget.annotation.ViewInject;
import widget.badge.BadgeView;
import widget.listener.OnKeyBoardStateChangedListener;

/**
 * Created by Mr.Fan on 2016/4/11.
 */
public class ViewUtils {

    public static void inject(Activity activity) {
        inject(activity, activity.getWindow().getDecorView());
    }

    public static void inject(Activity activity, Class clazz) {
        inject(activity, activity.getWindow().getDecorView(), clazz);
    }

    public static void inject(Object obj, View anchor) {
        inject(obj, anchor, obj.getClass());
    }

    public static void inject(Object obj, View anchor, Class clazz) {
        if (obj == null || anchor == null)
            return;
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            ViewInject inject = field.getAnnotation(ViewInject.class);
            if (inject != null) {
                int viewId = inject.value();
                int layoutId = inject.layout();
                if (viewId != -1 && viewId != 0) {
                    // 初始化View
                    try {
                        Method method = View.class.getDeclaredMethod("findViewById", int.class);
                        if (layoutId != 0) {
                            View view = anchor.findViewById(layoutId);
                            if (view != null)
                                anchor = view;
                        }
                        Object resView = method.invoke(anchor, viewId);
                        if (resView != null) {
                            field.setAccessible(true);
                            field.set(obj, resView);
                        }
                    } catch (Exception e) {
                        Logger.debug(e);
                    }
                }
            }
        }
    }

    public static Typeface getTypeFace(String asset) {
        try {
            Typeface typeFace = Typeface.createFromAsset(Library.getContext().getAssets(), asset);
            return typeFace;
        } catch (Exception e) {
            Logger.debug(e);
        }
        return null;
    }

    public static GradientDrawable getCornerDrawable(int bgColor, float radius) {
        return getCornerDrawable(bgColor, radius, 0, 0);
    }

    public static GradientDrawable getCornerDrawable(int bgColor, float radius, int stroke, int color) {
        return getCornerDrawable(bgColor, radius, true, true, true, true, stroke, color);
    }

    public static GradientDrawable getCornerDrawable(int bgColor, int radius, boolean leftTop, boolean rightTop,
                                                     boolean leftBottom, boolean rightBottom) {
        return getCornerDrawable(radius, bgColor, leftTop, rightTop, leftBottom, rightBottom, 0, 0);
    }

    public static GradientDrawable getCornerDrawable(int bgColor, float radius, boolean leftTop, boolean rightTop,
                                                     boolean leftBottom, boolean rightBottom, int strokeWidth, int strokeColor) {
        GradientDrawable gd = new GradientDrawable();
        gd.setColor(bgColor);
//            background.setCornerRadii(new float[]{
//                    topRadius, topRadius,
//                    topRadius, topRadius,
//                    bottomRadius, bottomRadius,
//                    bottomRadius, bottomRadius});
        float[] cs = new float[]{0, 0, 0, 0, 0, 0, 0, 0};
        if (leftTop) {
            cs[0] = radius;
            cs[1] = radius;
        }
        if (leftBottom) {
            cs[6] = radius;
            cs[7] = radius;
        }
        if (rightTop) {
            cs[2] = radius;
            cs[3] = radius;
        }
        if (rightBottom) {
            cs[4] = radius;
            cs[5] = radius;
        }
        gd.setCornerRadii(cs);
        if (strokeWidth > 0)
            gd.setStroke(strokeWidth, strokeColor);
        return gd;
    }

    public static void setTextFont(Context context, String fontName, View... views) {
        Typeface mtf = Typeface.createFromAsset(context.getAssets(), "fonts/" + (fontName.endsWith(".ttf") ? fontName : fontName + ".ttf"));
        for (View v : views)
            if (v instanceof TextView) {
                ((TextView) v).setTypeface(mtf);
            }
    }

    public static void setEditNoCopyAndPaste(EditText et) {
        if (et != null) {
            et.setLongClickable(false);
            et.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
            if (android.os.Build.VERSION.SDK_INT >= 11) {
                et.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
                    @Override
                    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                        return false;
                    }

                    @Override
                    public void onDestroyActionMode(ActionMode mode) {

                    }

                    @Override
                    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                        return false;
                    }

                    @Override
                    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                        return false;
                    }
                });
            }
        }
    }

    public static void drawTextInRect(Canvas canvas, String text, Paint paint) {
        drawTextInRect(canvas, new RectF(0, 0, canvas.getWidth(), canvas.getHeight()), text, paint);
    }

    public static void drawTextInRect(Canvas canvas, RectF rectf, String text, Paint paint) {
        if (canvas == null || rectf == null || TextUtils.isEmpty(text) || paint == null)
            return;
        Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();
        int baseline = (int) rectf.centerY() - (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.top;
        paint.setTextAlign(Paint.Align.CENTER);
        canvas.drawText(text, rectf.centerX(), baseline, paint);
    }

    public static SwipeRefreshLayout addSwipeRefresh2List(final ListView listView) {
        ViewGroup container = ((ViewGroup) listView.getParent());
        int count = container.getChildCount();
        int index = 0;
        for (int i = 0; i < count; i++) {
            if (listView.equals(container.getChildAt(i))) {
                index = i;
                break;
            }
        }

        final ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);

        final SwipeRefreshLayout refresh = new SwipeRefreshLayout(listView.getContext());
        final RelativeLayout rl = new RelativeLayout(listView.getContext());
        container.removeView(listView);
        rl.addView(listView, lp);
        refresh.addView(rl, lp);
        ViewGroup.LayoutParams trans_lp = listView.getLayoutParams();
        container.addView(refresh, index, trans_lp != null ? trans_lp : lp);
        listView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_MOVE) {
                    if (listView.getFirstVisiblePosition() == 0 && listView.getChildAt(0).getTop() >= listView.getListPaddingTop()) {
                        refresh.setEnabled(true);
                    } else refresh.setEnabled(false);
                }
                return false;
            }
        });
        return refresh;
    }

    public static TextView addEmptyView2List(final ListView listView, String text) {
        if (listView == null)
            return null;
        TextView textView = new TextView(listView.getContext());
        textView.setText(text);
        textView.setGravity(Gravity.CENTER);
        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 22);
        textView.setVisibility(View.GONE);
        textView.setMovementMethod(ScrollingMovementMethod.getInstance());

        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);

        ViewGroup vg = ((ViewGroup) listView.getParent());
        vg.addView(textView, lp);
        listView.setEmptyView(textView);
        return textView;
    }

    public static void addTopButton(final ListView listView, int resId) {
        final ImageView btn_top = addRightBottomButton(listView, resId,
                new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (listView != null) {
                            listView.smoothScrollToPosition(0);
                        }
                    }
                });
        listView.setOnScrollListener(new AbsListView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                if (btn_top != null)
                    btn_top.setVisibility(firstVisibleItem > 0 ? View.VISIBLE : View.GONE);
            }
        });
    }

    public static ImageView addRightBottomButton(View anchor, int bg, View.OnClickListener listener) {
        if (anchor == null)
            return null;
        try {
            ViewGroup group = (ViewGroup) (anchor instanceof ViewGroup ? anchor : anchor.getParent());
            while (group != null && !(group instanceof RelativeLayout)) {
                group = (ViewGroup) group.getParent();
            }
            if (group != null && group instanceof RelativeLayout) {
                final RelativeLayout layout = (RelativeLayout) group;
                final Context context = layout.getContext();
                final ImageView button = new ImageView(context);
                button.setImageResource(bg);
                button.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
                final RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
                lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
                lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
                lp.rightMargin = DisplayUtil.dp2px(15);
                lp.bottomMargin = DisplayUtil.dp2px(15);
                lp.width = DisplayUtil.dp2px(30);
                lp.height = DisplayUtil.dp2px(30);
                layout.addView(button, lp);

                button.setOnClickListener(listener);
                return button;
            }
        } catch (Exception e) {
            Logger.debug(e);
        }
        return null;
    }

    public static View addRightBottomView(View anchor, View view, View.OnClickListener listener) {
        if (anchor == null)
            return null;
        try {
            ViewGroup group = (ViewGroup) (anchor instanceof ViewGroup ? anchor : anchor.getParent());
            while (group != null && !(group instanceof RelativeLayout)) {
                group = (ViewGroup) group.getParent();
            }
            if (group != null && group instanceof RelativeLayout) {
                final RelativeLayout layout = (RelativeLayout) group;
                final Context context = layout.getContext();
                final RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
                lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
                lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
                lp.rightMargin = DisplayUtil.dp2px(15);
                lp.bottomMargin = DisplayUtil.dp2px(15);
                lp.width = DisplayUtil.dp2px(30);
                lp.height = DisplayUtil.dp2px(30);
                layout.addView(view, lp);

                view.setOnClickListener(listener);
                return view;
            }
        } catch (Exception e) {
            Logger.debug(e);
        }
        return null;
    }

    public static View getInvisibleView(Context context, float height) {
        LinearLayout ll = new LinearLayout(context);
        View v = new View(context);
        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        lp.width = DisplayUtil.getMetrics().widthPixels;
        lp.height = DisplayUtil.dp2px(height);
        ll.addView(v, lp);
        ll.setVisibility(View.INVISIBLE);
        return ll;
    }

    public static RectF getImageViewRectF(ImageView iv) {
        RectF rectf = null;
        try {
            Matrix matrix = iv.getImageMatrix();
            Drawable d = iv.getDrawable();
            if (d != null) {
                Rect rect = d.getBounds();
                float[] values = new float[9];
                matrix.getValues(values);
                rectf = new RectF(values[2], values[5],
                        values[2] + rect.width() * values[0],
                        values[5] + rect.height() * values[0]
                );
            }
        } catch (Exception e) {
            Logger.debug(e);
        }
        return rectf;
    }

    public static RectF getTextViewRectF(TextView tv) {
        Rect rect;
        try {
            String text = tv.getText().toString();
            TextPaint paint = tv.getPaint();
            rect = new Rect();
            paint.getTextBounds(text, 0, text.length(), rect);
        } catch (Exception e) {
            Logger.debug(e);
            rect = null;
        }
        return rect != null ? new RectF(rect) : null;
    }

    public static BadgeView addBadge(final View v) {
        if (v == null) {
            Logger.debug(new Exception("add badge on null view"));
            return null;
        }
        ViewGroup group = (ViewGroup) v.getParent();
        while (group != null && !(group instanceof RelativeLayout)) {
            group = (ViewGroup) group.getParent();
        }
        if (group == null || !(group instanceof RelativeLayout)) {
            Logger.debug(new Exception("add badge on unexpected group:" + (group == null ? group : group.getClass().getSimpleName())));
            return null;
        }
        int count = group.getChildCount();
        for (int i = 0; i < count; i++) {
            if (group.getChildAt(i) instanceof BadgeView) {
                return (BadgeView) group.getChildAt(i);
            }
        }
        RelativeLayout anchor = (RelativeLayout) group;
        BadgeView point = null;
        if (v instanceof ImageView) {
            ImageView iv = (ImageView) v;
            RectF rectf = ViewUtils.getImageViewRectF(iv);
            if (rectf != null) {
                float point_margin_top = (iv.getHeight() - rectf.height()) / 2;
                float point_margin_right = (iv.getWidth() - rectf.width()) / 2;
                point = addBadgeAtMarginRight(anchor, point_margin_top, point_margin_right);
            }
        } else if (v instanceof TextView) {
            try {
                TextView tv = (TextView) v;
                RectF rectf = ViewUtils.getTextViewRectF(tv);
                if (rectf != null && !TextUtils.isEmpty(tv.getText())) {
                    float point_margin_top = (anchor.getHeight() - rectf.height()) / 2;
                    float point_margin_right = (anchor.getWidth() - rectf.width()) / 2;
                    point = addBadgeAtMarginRight(anchor, point_margin_top, point_margin_right);
                }
            } catch (Exception e) {
                Logger.debug(e);
            }
        } else {
            Logger.debug(new Exception("add badge on unexpected anchor:" + (v == null ? v : v.getClass().getSimpleName())));
        }
//        int margin = DisplayUtil.dp2px(getContext(), 3);
//        addPointAtMarginRight(margin, margin);
        return point;
    }

    private static BadgeView addBadgeAtMarginRight(RelativeLayout anchor, float margin_top, float margin_right) {
        final Context context = anchor.getContext();
        BadgeView point = new BadgeView(context);
        int dimen = DisplayUtil.dp2px(8);
        RelativeLayout.LayoutParams lp_point = new RelativeLayout.LayoutParams(dimen, dimen);
        lp_point.topMargin = (int) margin_top;
        lp_point.rightMargin = (int) margin_right;
        lp_point.addRule(RelativeLayout.ALIGN_PARENT_TOP);
        lp_point.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);

        anchor.addView(point, lp_point);
        point.setVisibility(View.INVISIBLE);
        return point;
    }


    /**
     * 1，将SoftInputMode设置为ADJUST_RESIZE
     * 2，并适配一个view对软键盘弹出的高度
     * 3，当系统软键盘弹出时，如果目标view被软键盘覆盖，则调整该view高度
     * 4，当软键盘消失后，恢复view原始高度
     *
     * @param view     适配的目标view
     * @param listener 监听回叼接口
     */
    public static void assistKeyboard(final View view, final OnKeyBoardStateChangedListener listener) {
        if (view == null)
            return;
        if (view.getContext() instanceof Activity)
            ((Activity) view.getContext()).getWindow().setSoftInputMode(
                    WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN
                            | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
            );
        view.getViewTreeObserver().addOnGlobalLayoutListener(new KeyBoardListener(view, listener));
    }

    /**
     * 0，SoftInputMode 为 ADJUST_RESIZE 有效
     * 1，使用getWindowVisibleDisplayFrame获取屏幕显示Rect
     * 2，软件盘弹出时，WindowVisibleDisplayFrame 矩阵会变化
     * 3，根据该变化判断键盘高度
     * 4，Dialog 全屏弹窗时，可能也会引起变化
     * 5，所以使用bottom来判断，比使用height() 来判断相对更准确一些
     */
    private static final class KeyBoardListener implements ViewTreeObserver.OnGlobalLayoutListener {
        //目标view弱引用
        private final WeakReference<View> reference;
        //监听回调
        private final OnKeyBoardStateChangedListener listener;
        private final int deviceHeight;
        //程序可视高度（不包含键盘）
        private int windowVisibleHeight;
        //目标view原始高度属性值
        private Integer targetLpHeight;

        public KeyBoardListener(@NonNull View view, OnKeyBoardStateChangedListener listener) {
            this.listener = listener;
            this.reference = new WeakReference<>(view);
            this.deviceHeight = DisplayUtil.getMetrics().heightPixels;
        }

        @Override
        public void onGlobalLayout() {
            final View target = reference.get();
            if (target == null)
                return;
            final Rect rWindow = getWindowVisibleRect(target);
            final Rect rTarget = getTargetVisibleRect(target);
            final ViewGroup.LayoutParams lp = target.getLayoutParams();
            //初始化
            if (targetLpHeight == null) {
                targetLpHeight = lp.height;
                windowVisibleHeight = rWindow.height();
                return;
            }
            if (windowVisibleHeight != rWindow.height()) {
                //是否显示键盘
                final boolean isShow = rWindow.bottom < deviceHeight;
                //获取键盘高度
                final int kbHeight = windowVisibleHeight - rWindow.height();

                if (isShow) {
                    if (rTarget.bottom > rWindow.height() || lp.height != targetLpHeight.intValue()) {
                        lp.height = rWindow.bottom - rTarget.top;
                        target.requestLayout();
                    }
                } else {
                    if (lp.height != targetLpHeight.intValue()) {
                        lp.height = targetLpHeight.intValue();
                        target.requestLayout();
                    }
                }
//                Logger.simple("WindowVisible : " + rWindow.top + " | " + rWindow.bottom);
//                Logger.simple("TargetVisible : " + rTarget.top + " | " + rTarget.bottom);
//                Logger.simple("kb :" + isShow + " + / Height : " + kbHeight);
//                Logger.simple(lp.height + "=" + rWindow.bottom + "-" + rTarget.top);
                if (listener != null) {
                    listener.onStateChange(isShow);
                }
                windowVisibleHeight = rWindow.height();
            }
        }

        /**
         * 获取程序显示区域矩阵，
         * 不包含系统状态栏和导航栏
         *
         * @param v
         * @return
         */
        private Rect getWindowVisibleRect(View v) {
            final Rect r = new Rect();
            v.getWindowVisibleDisplayFrame(r);
            return r;
        }

        /**
         * 获取目标view在屏幕中所占显示的矩阵
         *
         * @param v
         * @return
         */
        private Rect getTargetVisibleRect(View v) {
            final Rect r = new Rect();
            v.getGlobalVisibleRect(r);
            return r;
        }
    }

}